#include <QtGui/QMouseEvent>
//#include <QtGui/QAction>
#include <QtCore/QList>
#include <QtCore/QTimer>
#include <QTextCodec>
#include <QtCore/QHash>
#include <QtWebKit/QWebHistory>
#include "operator.h"
#include "browser.h"
#include "../activite/webcollector.h"
#include <boost/regex.hpp>

Operator::Operator(WebPage* page, WebSpiderRequest *req, QObject *parent)
    : QObject(parent)
    , m_page(page)
    , m_parseSuccess(false)
    , m_requestManager(WebSpiderRequestManager::instance())
    , m_request(req)
    , m_downloadRequest(NULL)
    , m_thisPoint(long(this))
    , m_loadSucess(false)
    , m_hostNotFound(false)
    , m_HTTPRequestCanceled(false)
    , m_netWorkError(false)
    , m_protocolUnknown(false)
    , m_destroying(false)
    , m_syncLoad(false)
    , m_isTimeout(false)
    , m_hasParsed(false)
    , m_errorCode(0)
{
    connect(&m_selfDestroyTimer, SIGNAL(timeout()), this, SLOT(selfDestroy()));
    connect(&m_timeoutTimer, SIGNAL(timeout()), this, SLOT(onTimeout())); 

    // default page size.
    QVariantMap size;
    size.insert("width", 1280);
    size.insert("height", 1024);
    m_page->setViewportSize(size);
}

Operator::~Operator()
{
    qDebug() << "Delete operator :" << this;
}

WebSpiderRequest* Operator::request()
{
    return m_request;
}

QString Operator::title()
{
    return m_page->title();
}

QString Operator::frameTitle()
{
    return m_page->frameTitle();
}

QString Operator::getUrl()
{
    return m_page->url();
}

QString Operator::frameUrl()
{
    return m_page->frameUrl();
}

QString Operator::toHtml()
{
    return m_page->mainFrame()->toHtml();
}

bool Operator::loading()
{
    return m_page->loading();
}

int Operator::loadingProgress()
{
    return m_page->loadingProgress();
}

void Operator::reload()
{
    m_page->reload();
}

void Operator::goBack()
{
    m_page->goBack();
}

void Operator::goForward()
{
    m_page->goForward();
}

void Operator::setParseSuccess()
{
    m_parseSuccess = true;
}

void Operator::clearParseSuccess()
{
    m_parseSuccess = false;
}

bool Operator::isParseSuccess()
{
    return m_parseSuccess;
}

void Operator::remove()
{
    if (m_destroying)
        return;

    m_destroying = true;

    stopTimeoutTimer();

    if (m_selfDestroyTimer.isActive())
        m_selfDestroyTimer.stop();

    destory();
}

bool Operator::destroying()
{
    return m_destroying;
}

QWebElementCollection Operator::findElements(QString selectorQuery)
{
    QWebFrame *pFrame = m_page->mainFrame();
    QWebElementCollection collection = pFrame->findAllElements(selectorQuery);
    return collection;
}

QWebElement Operator::findElement(QString selectorQuery)
{
    QWebElement element = m_page->mainFrame()->findFirstElement(selectorQuery);
    return element;
}

QString Operator::getText(QWebElement &element)
{
    return element.toPlainText();
}

QString Operator::getInnerXml(QWebElement &element)
{
    return element.toInnerXml();
}

QString Operator::getOuterXml(QWebElement &element)
{
    return element.toOuterXml();
}

QString Operator::getAttribute(QWebElement &element, QString attr)
{
    return element.attribute(attr);
}

void Operator::setAttribute(QWebElement &element, QString attr, QString value)
{
    element.setAttribute(attr, value);
}

void Operator::setViewportSize(int w, int h)
{
    QVariantMap size;
    size.insert("width", w);
    size.insert("height", h);
    m_page->setViewportSize(size);
}

// 直接放到全屏大小, click完再还原.
void Operator::click(QWebElement &element)
{
    QSize contentsSize = m_page->mainFrame()->contentsSize();

    QSize viewportSize = m_page->customPageViewportSize();

    m_page->setCustomPageViewportSize(contentsSize.width(), contentsSize.height());

    QRect elGeom=element.geometry();
    QRect elementRect(QPoint(0, 0), QSize(elGeom.width(), elGeom.height()));
    m_page->click(QPoint(elGeom.x()+1, elGeom.y()+1));

    m_page->setCustomPageViewportSize(viewportSize.width(), viewportSize.height());
}

void Operator::startSelfDestroyTimer()
{
    startSelfDestroyTimer(request()->selfDestroyTime());
}

void Operator::startSelfDestroyTimer(long timeMs)
{
    if (timeMs <= 0)
        return ;

    m_selfDestroyTimer.start(timeMs);
    m_selfDestroyTimer.setSingleShot(true);
}

void Operator::startTimeoutTimer()
{
    startTimeoutTimer(request()->timeoutTime());
}

void Operator::startTimeoutTimer(long timeMs)
{
    if (timeMs <= 0)
        return ;

    if (m_timeoutTimer.isActive())
        return;

    m_isTimeout = false;
    m_timeoutTimer.start(timeMs);
    m_timeoutTimer.setSingleShot(true);
}

void Operator::stopTimeoutTimer()
{
    if (!m_timeoutTimer.isActive())
        return;

    m_timeoutTimer.stop();
}

void Operator::loadUrl( const QString &address,
                        const QString &operation,
                        const QMap<QString, QString> &headers,
                        QString bodyString, QString encoding,
                        int timeout,
                        int dtimeout )
{
    request()->setTimeoutTime(timeout);
    request()->setSelfDestroyTime(dtimeout);
    startTimeoutTimer();

	m_page->loadUrl(address, operation, headers, bodyString, encoding);
}

void Operator::setHtml(const QString &html, const QString &url)
{
	m_page->mainFrame()->setHtml(html, url);
}

void Operator::setSetting(const QString &name, const QString &setting)
{
	bool ok;

	if (name == PAGE_SETTINGS_LOAD_IMAGES
		|| name == PAGE_SETTINGS_JS_ENABLED
		|| name == PAGE_SETTINGS_XSS_AUDITING
		|| name == PAGE_SETTINGS_LOCAL_ACCESS_REMOTE
		|| name == PAGE_SETTINGS_WEB_SECURITY_ENABLED
		|| name == PAGE_SETTINGS_JS_CAN_OPEN_WINDOWS
		|| name == PAGE_SETTINGS_JS_CAN_CLOSE_WINDOWS ) {
		bool val = setting.toInt(&ok) == 0 ? false : true;
		QVariant var = QVariant::fromValue(val);
		m_page->setSetting(name, var);
	} else if (name == PAGE_SETTINGS_USER_AGENT
			|| name == PAGE_SETTINGS_USERNAME
			|| name == PAGE_SETTINGS_PASSWORD ) {
		QVariant var = QVariant::fromValue(setting);
		m_page->setSetting(name, var);
	} else if (name == PAGE_SETTINGS_MAX_AUTH_ATTEMPTS
			|| name == PAGE_SETTINGS_RESOURCE_TIMEOUT) {
		int val = setting.toInt(&ok);
		QVariant var = QVariant::fromValue(val);
		m_page->setSetting(name, var);
	}
}

bool Operator::setDatabaseEnabled(long ref, bool enable)
{
}

QString Operator::getTitle(long ref)
{
    if (ref != m_thisPoint)
        return "";
    return title();
}

QString Operator::renderTreeDump(long ref)
{
    if (ref != m_thisPoint)
        return "";
    //NOTE: QT 5 webframe has no renderTreeDump.
    return m_page->mainFrame()->renderTreeDump();
}

QString Operator::getDomTreeSource(long ref)
{
    if (ref != m_thisPoint)
        return "";
    QWebElement element = m_page->mainFrame()->documentElement();
    return element.toInnerXml();
}

QString Operator::getPageSource(long ref)
{
    if (ref != m_thisPoint)
        return "";
    return toHtml();
}

QString Operator::getUrl(long ref)
{
    if (ref != m_thisPoint)
        return "";
    return getUrl();
}

//object Operator::evaluateJS(object driver, long ref, objectArray arguments)
//{
//}

//Document
long Operator::getElementById(long ref, QString query)
{
    if (ref != m_thisPoint)
        return 0;

    QString idQuery = "[id=\"";
    idQuery += query;
    idQuery += "\"]";

    QWebElement e = findElement(idQuery);
    if (e.isNull())
        return 0;

    QWebElement *element = new QWebElement(e);
    m_elementParms.push_back(element);
    return (long)element;
}

long Operator::setCookies(long ref, const QVariantList &cookies)
{
    if (ref != m_thisPoint)
        return 0;
    m_page->setCookies(cookies);
}

QVariantList Operator::cookies(long ref)
{
    if (ref != m_thisPoint)
        return QVariantList();

    return m_page->cookies();
}

long Operator::deleteCookie(long ref, QString cName)
{
    if (ref != m_thisPoint)
        return 0;

    if (m_page->deleteCookie(cName))
        return 1;

    return 0;
}

//Node
QList<long> Operator::getElementsByTagName(long ref, QString query)
{
    QList<long> ret;

    if (ref != m_thisPoint)
        return ret;
    QWebElementCollection collection = findElements(query);
    for (int i = 0; i < collection.count(); i ++) {
        QWebElement *element = new QWebElement(collection.at(i));
        m_elementParms.push_back(element);
        ret.push_back((long)element);
    }
    return ret;
}

// body > div > div > div > input[type="checkbox"]
QList<long> Operator::getElementsByName(long ref, QString query)
{
    QList<long> ret;

    if (ref != m_thisPoint)
        return ret;
    QString nameCss = "[name=\"";
    nameCss += query;
    nameCss +=  "\"]";
    QWebElementCollection collection = findElements(nameCss);
    for (int i = 0; i < collection.count(); i ++) {
        QWebElement *element = new QWebElement(collection.at(i));
        m_elementParms.push_back(element);
        ret.push_back((long)element);
    }
    return ret;
}

QList<long> Operator::getElementsByXpath(long ref, QString query)
{
}

long Operator::getElementByCssSelector(long ref, QString query)
{
    if (ref != m_thisPoint)
        return 0;

    QWebElement e = findElement(query);
    if (e.isNull())
        return 0;

    QWebElement *element = new QWebElement(e);
    m_elementParms.push_back(element);
    return (long)element;
}

QList<long> Operator::getElementsByCssSelector(long ref, QString query)
{
    QList<long> ret;

    if (ref != m_thisPoint)
        return ret;
    QWebElementCollection collection = findElements(query);
    for (int i = 0; i < collection.count(); i ++) {
        QWebElement *element = new QWebElement(collection.at(i));
        m_elementParms.push_back(element);
        ret.push_back((long)element);
    }
    return ret;
}

//Element
QList<long> Operator::getChildsByCssSelector(long ref, QString query)
{
    QList<long> ret;
    QWebElement* element = (QWebElement*)ref;

    if (!m_elementParms.contains(element))
        return ret;

    QWebElementCollection collection = element->findAll(query);
    for (int i = 0; i < collection.count(); i ++) {
        QWebElement *element = new QWebElement(collection.at(i));
        m_elementParms.push_back(element);
        ret.push_back((long)element);
    }
    return ret;
}

QString Operator::getTagName(long ref)
{
    QWebElement* element = (QWebElement*)ref;
    if (!m_elementParms.contains(element))
        return "";
    return element->tagName();
}

QString Operator::getAttribute(long ref, QString name)
{
    QWebElement* element = (QWebElement*)ref;
    if (!m_elementParms.contains(element))
        return "";
    return element->attribute(name);
}

void Operator::setAttribute(long ref, QString name, QString value)
{
    QWebElement* element = (QWebElement*)ref;
    if (!m_elementParms.contains(element))
        return ;
    element->setAttribute(name, value);
}

void Operator::removeAttribute(long ref, QString name)
{
    QWebElement* element = (QWebElement*)ref;
    if (!m_elementParms.contains(element))
        return ;
    element->removeAttribute(name);
}

QString Operator::getProperty(long ref, QString name)
{
    QWebElement* element = (QWebElement*)ref;
    if (!m_elementParms.contains(element))
        return "";
    return element->styleProperty(name, QWebElement::InlineStyle);
}

QString Operator::getText(long ref)
{
    QWebElement* element = (QWebElement*)ref;
    if (!m_elementParms.contains(element))
        return "";
    return element->toPlainText();
}

QString Operator::getValue(long ref)
{
    QWebElement* element = (QWebElement*)ref;
    if (!m_elementParms.contains(element))
        return "";
    return element->attribute("value");
}

long Operator::type(long ref, QString val)
{
    QWebElement* element = (QWebElement*)ref;
    if (!m_elementParms.contains(element))
        return 0;
    click(*element);
    element->setFocus();
    QVariant text(val);
    m_page->sendEvent(QString("keypress"), text, QVariant(), QString(), Qt::NoModifier);
    return 1;
}

long Operator::sendKeys(long ref, QString type, long keys, int timeout, int dtimeout, WebSpiderSyncRequest *req)
{
    if (req) {
        req->setTimeoutTime(timeout);
        req->setSelfDestroyTime(dtimeout);
        req->setRequestOpt(this);

        req->startWaitTimer(timeout);
        setRawRequest(req);
    } else {
        request()->setTimeoutTime(timeout);
        startTimeoutTimer();
    }

    QWebElement* element = (QWebElement*)ref;
    if (!m_elementParms.contains(element))
        return 0;
    element->setFocus();
    QVariant keyText((int)keys);
    m_page->sendEvent(type, keyText, QVariant(), QString(), Qt::NoModifier);
    return 1;
}

long Operator::setValue(long ref, QString val)
{
    QWebElement* element = (QWebElement*)ref;
    if (!m_elementParms.contains(element))
        return 0;
    element->setAttribute("value", val);
    return 1;
}

void Operator::goBack(long ref)
{
    if (ref != m_thisPoint)
        return;
    goBack();
}

void Operator::goForward(long ref)
{
    if (ref != m_thisPoint)
        return;
    goForward();
}

long Operator::refresh(long ref)
{
    if (ref != m_thisPoint)
        return 0;
    reload();
    return 1;
}

long Operator::submit(long ref)
{
    QWebElement* element = (QWebElement*)ref;
    if (!m_elementParms.contains(element))
        return 0;
    element->setFocus();
    element->evaluateJavaScript("click()");
    return 1;
}

long Operator::click(long ref, int timeout, int dtimeout, WebSpiderSyncRequest *req)
{
    if (req) {
        req->setTimeoutTime(timeout);
        req->setSelfDestroyTime(dtimeout);
        req->setRequestOpt(this);

        req->startWaitTimer(timeout);
        setRawRequest(req);
    } else {
        request()->setTimeoutTime(timeout);
        startTimeoutTimer();
    }

    QWebElement* element = (QWebElement*)ref;
    if (!m_elementParms.contains(element))
        return 0;
    click(*element);
    return 1;
}

void Operator::mouseMove(int x, int y)
{
    m_page->mouseMove(QPoint(x, y));
}

void Operator::mouseMove(long ref)
{
    QWebElement* element = (QWebElement*)ref;
    if (!m_elementParms.contains(element))
        return ;

    QRect elGeom = element->geometry();
    m_page->mouseMove(QPoint(elGeom.x() + 2, elGeom.y() + 2));
}

long Operator::download(long ref, WebSpiderDownloadRequest *req)
{
    m_downloadRequest = req;

    QWebElement* element = (QWebElement*)ref;
    if (!m_elementParms.contains(element))
        return 0;

    click(*element);

    return 1;
}

long Operator::toggle(long ref)
{
    QWebElement* element = (QWebElement*)ref;
    if (!m_elementParms.contains(element))
        return 0;

    if (element->hasAttribute("display")) {
        QString value = element->attribute("display");
        if (value == "none")
            element->setAttribute("display", "block");
        else if (value == "block")
            element->setAttribute("display", "none");
    } else {
        element->setAttribute("display", "none");
    }

    return 1;
}

long Operator::focus(long ref)
{
    QWebElement* element = (QWebElement*)ref;
    if (!m_elementParms.contains(element))
        return 0;
    element->setFocus();
    return 1;
}

long Operator::selected(long ref)
{
    QWebElement* element = (QWebElement*)ref;
    if (!m_elementParms.contains(element))
        return 0;
    QVariant ret = element->evaluateJavaScript("this.selected");
    if (ret.isNull())
        return 0;
    if (ret.type() == QVariant::Bool)
        return ret.toInt();

    return 1;
}

long Operator::setSelected(long ref, bool selected)
{
    QWebElement* element = (QWebElement*)ref;
    if (!m_elementParms.contains(element))
        return 0;
    if (selected)
        element->evaluateJavaScript("this.selected = true");
    else
        element->evaluateJavaScript("this.selected = false");
    return 1;
}

long Operator::checked(long ref)
{
    QWebElement* element = (QWebElement*)ref;
    if (!m_elementParms.contains(element))
        return 0;
    QVariant ret = element->evaluateJavaScript("this.checked");
    if (ret.isNull())
        return 0;
    if (ret.type() == QVariant::Bool)
        return ret.toInt();

    return 1;
}

long Operator::setChecked(long ref, bool checked)
{
    QWebElement* element = (QWebElement*)ref;
    if (!m_elementParms.contains(element))
        return 0;
    if (checked)
        element->evaluateJavaScript("this.checked = true");
    else
        element->evaluateJavaScript("this.checked = false");
    return 1;
}

long Operator::selectOptionByIndex(long ref, long index)
{
    QWebElement* element = (QWebElement*)ref;
    if (!m_elementParms.contains(element))
        return 0;
    QString js = "this.options[" + QString::number(index) + "].selected = true";
    element->evaluateJavaScript(js);
    return 1;
}

long Operator::stale(long ref)
{
    QWebElement* element = (QWebElement*)ref;
    if (!m_elementParms.contains(element))
        return 0;
    return element->isNull();
}

long Operator::activeElement()
{
    QWebElementCollection all = m_page->mainFrame()->findAllElements("*");
    QWebElement *element = NULL;

    Q_FOREACH(QWebElement e, all) {
        if (e.hasFocus())
            element = new QWebElement(e);
    }

    if (element)
        m_elementParms.push_back(element);
    return (long)element;
}

long Operator::selectFrameByName(QString name)
{
}

long Operator::selectFrameByIdx(long idx)
{
}

bool Operator::isVisible(long ref)
{
    QWebElement* element = (QWebElement*)ref;
    if (!m_elementParms.contains(element))
        return false;

    if (element->hasAttribute("display")) {
        if (element->attribute("display") == "none")
            return false;
    } else if (element->hasAttribute("visibility")) {
        if (element->attribute("visibility") == "hidden")
            return false;
    }

    QString styleDisplay = element->styleProperty("display", QWebElement::InlineStyle);
    if(styleDisplay == "none")
        return false;

    return true;
}

long Operator::drag(long ref, int toX, int toY, int duration)
{
}

QRect Operator::getRect(long ref)
{
    QWebElement* element = (QWebElement*)ref;

    if (!m_elementParms.contains(element))
        return QRect();

    return element->geometry();
}

QString Operator::getAlertText(long ref, bool clr)
{
    return m_lastAlertString;
}

void Operator::scroll(int x, int y)
{
    m_page->mainFrame()->scroll(x, y);
}

void Operator::setFilterRule(QList<QString> rules)
{
    m_request->setResourceFilterRule(rules);
}

void Operator::appendFilterRule(QString rule)
{
    m_request->appendResourceFilterRule(rule);
}

void Operator::removeFilterRule(QString rule)
{
    m_request->removeResourceFilterRule(rule);
}

bool Operator::matchFilter(QString resource)
{
    QList<QString> rules = m_request->resourceFilterRules();

    QList<QString>::iterator it;
    for (it = rules.begin(); it != rules.end(); it++) {
        try {
            boost::regex reg(it->toLatin1().constData());
            if (boost::regex_match(resource.toLatin1().constData(), reg))
                return true;
        } catch(boost::exception& e) {
        }
    }

    return false;
}

// slots
void Operator::selfDestroy()
{
    qDebug() << "Self Destory:" << getUrl();
    Q_EMIT didSelfDestroyParse();
}

void Operator::onTimeout()
{
    setTimeout();
    Q_EMIT didTimeout();
}

QString Operator::getInnerXml(long ref)
{
    QWebElement* element = (QWebElement*)ref;
    if (!m_elementParms.contains(element))
        return "";
    return element->toInnerXml();
}

QString Operator::getOuterXml(long ref)
{
    QWebElement* element = (QWebElement*)ref;
    if (!m_elementParms.contains(element))
        return "";
    return element->toOuterXml();
}

QString Operator::renderBase64()
{
    return m_page->renderBase64();
}

QString Operator::renderBase64(long ref)
{
    QWebElement* element = (QWebElement*)ref;
    if (!m_elementParms.contains(element))
        return "";
    return m_page->renderBase64("png", element);
}

void Operator::setSelfDestroyTime(long timeMS)
{
    qDebug() << "set destroy time : " << timeMS;
    request()->setSelfDestroyTime(timeMS);
    startSelfDestroyTimer();
}

bool Operator::loadSuccess()
{
    return m_loadSucess; 
}

void Operator::setLoadSuccess()
{
    m_loadSucess = true;
}

void Operator::clearLoadSuccess()
{
    m_loadSucess = false;
}

void Operator::setLastAlertString(QString str)
{
    m_lastAlertString = str;
}

void Operator::setHostNotFound()
{
    m_hostNotFound = true;
}

bool Operator::hostNotFound()
{
    return m_hostNotFound;
}

void Operator::setHTTPRequestCanceled()
{
    m_HTTPRequestCanceled = true;
}

bool Operator::httpRequestCanceled()
{
    return m_HTTPRequestCanceled;
}

void Operator::setProtocolUnknown()
{
    m_protocolUnknown = true;
}

bool Operator::protocolUnknown()
{
    return m_protocolUnknown;
}

void Operator::setRawPageFlag()
{
    m_page->setRawPageFlag(reinterpret_cast<long>(this));
}

long Operator::rawPageFlag()
{
    return m_page->rawPageFlag();
}

void Operator::setRawRequest(WebSpiderSyncRequest* request)
{
    m_page->setRawRequest(request);
}

WebSpiderSyncRequest* Operator::rawRequest()
{
    return m_page->rawRequest();
}

void Operator::setNetWorkError()
{
    m_netWorkError = true;
}

bool Operator::netWorkError()
{
    return m_netWorkError;
}

void Operator::setErrorCode(int errorCode)
{
    m_errorCode = errorCode;
}

int Operator::errorCode()
{
    return m_errorCode;
}

void Operator::setSyncLoad()
{
    m_syncLoad = true;
}

void Operator::clearSyncLoad()
{
    m_syncLoad = false;
}

bool Operator::syncLoad()
{
    return m_syncLoad;
}

void Operator::destory()
{
    WebCollector::instance()->removeActiviteByOpt(this);
}

long Operator::selfDestroyTime()
{
    return request()->selfDestroyTime();
}

long Operator::timeoutTime()
{
    return request()->timeoutTime();
}

void Operator::setTimeout()
{
    m_isTimeout = true;
}

bool Operator::isTimeout()
{
    return m_isTimeout;
}

void Operator::setParsed()
{
    m_hasParsed = true;
}

void Operator::clearParsed()
{
    m_hasParsed = false;
}

bool Operator::hasParsed()
{
    return m_hasParsed;
}

void Operator::setPostData(QString data)
{
    m_postData = data;
}

QString Operator::postData()
{
    return m_postData;
}

void Operator::setMethod(QString method)
{
    m_method = method;
}

QString Operator::method()
{
    return m_method;
}

void Operator::release()
{
    for (int i = 0; i < m_elementParms.size(); i ++)
        delete m_elementParms.at(i);

    m_elementParms.clear();

    qDebug() << "Operator release!";
    deleteLater();
}

WebSpiderDownloadRequest* Operator::getDownloadRequest()
{
    return m_downloadRequest;
}

void Operator::clearDownloadRequest()
{
    m_downloadRequest = NULL;
}

std::string Operator::clientID()
{
    return m_request->clientID();
}

